#include "jadro.h"
#include "unif.h"
#include "synt_an.h"
#include "pamet.h"
#include "qconst.h"
#include "q_io.h"

// prvni parametr smi obsahovat promenne, druhy ne!
static int unif_atom(Qstrom*,Qstrom*,Promenne*);
static int unif_strom(Qstrom*,Qstrom*,Promenne*,Volba*&);
static int unif_seznam(Qstrom*,Qstrom*,Promenne*,Volba*&);
static int unif_seznamprom(Qstrom*,Qstrom*,Qstrom*&,int,Promenne*,Volba*&);
static int unif_retezec(Qhrana*,Qhrana*,Promenne*,Volba*&);
static int delka_seznamu(Qstrom*);
static Qstrom* posun_vseznamu(Qstrom*,int);


Promenna* Promenne::promenna(char* j)
{
  Promenna* p=prom;
  while(p)
  {
    if(!stricmp(p->vjmeno(),j)) return p;
    p=p->dalsi();
  }
  return NULL;
}

void Promenne::smaz(Promenna* p)
{
  Promenna *r=prom,*s;
  while(r!=p)
  {
    s=r->dalsi();
    delete r;
    r=s;
  }
  prom=p;
}

void Promenne::vypis(int)
{
  Promenna* p=prom;
  while(p)
  {
    QIO<<"$"<<p->vjmeno()<<" = ";
    QFILE.sethandle(stdout);
    if(p->delka()!=0) { p->vobsah()->test(NULL,p->vtyp(),p->delka()); QIO<<endln; } else QIO<<"-NUL-"<<endln;
    p=p->dalsi();
  }
}

int unif_atom(Qstrom* qu1,Qstrom* qu2,Promenne* pr)
{
  if(!qu1->promenna() && !qu2->promenna() && !qu1->nul() && !qu2->nul())
    if(!stricmp(qu1->vobsah(),qu2->vobsah())) return 1; else return 0;
  else if(qu1->promenna()==_Latomovapromenna)
  {
    Promenna* pp=pr->promenna(qu1->vobsah());
    if(!pp) { pr->pridat(qu1->vobsah(),qu2,_Latomovapromenna); return 1; }
    else return unif_atom(pp->vobsah(),qu2,pr);
  }
  else return 0;
}

int unif_strom(Qstrom* qu1,Qstrom* qu2,Promenne* pr,Volba*& vl)
{
  if(qu1->promenna()==_Lstromovapromenna)
  {
    Promenna* pp=pr->promenna(qu1->vobsah());
    if(!pp) { pr->pridat(qu1->vobsah(),qu2,_Lstromovapromenna); return 1; }
    else return unif_strom(pp->vobsah(),qu2,pr,vl);
  }
  else if(!unif_atom(qu1,qu2,pr)) return 0;
  else return unif_seznam(qu1->vsyn(),qu2->vsyn(),pr,vl);
}

int unif_seznam(Qstrom* qu1,Qstrom* qu2,Promenne* pr,Volba*& vl)
{
  if(!qu1 && !qu2) return 1;
  else if(qu1 && !qu2 && qu1->promenna()!=_Lseznamovapromenna || !qu1 && qu2) return 0;
  else if(qu1->promenna()==_Lseznamovapromenna)
  {
    Promenna* pp=pr->promenna(qu1->vobsah());
    Promenna* ps=pr->pozice();
    if(!pp || vl && !strcmp(qu1->vobsah(),vl->jmeno()))
    {
      Qstrom* qu3;
      if(!vl)
      {
	vl=new Volba(delka_seznamu(qu2)+1,qu1->vobsah(),ps);
	malopameti(vl);
      } else pr->smaz(vl->pozice());
      if(!vl->dal) vl->zmensit();
      while(vl->delka()>0)
      {
	pr->pridat(qu1->vobsah(),qu2,_Lseznamovapromenna,vl->delka());
	qu3=posun_vseznamu(qu2,vl->delka());
	if(unif_seznam(qu1->vbratr(),qu3,pr,vl->dal)) return 1;
	else { if(vl->dal) delete vl->dal; pr->smaz(vl->pozice()); }
	vl->zmensit();
      }
      pr->pridat(qu1->vobsah(),NULL,_Lseznamovapromenna,0);
      if(vl) { delete vl; vl=NULL; }
      return unif_seznam(qu1->vbratr(),qu2,pr,vl);
    } else {
      if(pp->delka()==0) return unif_seznam(qu1->vbratr(),qu2,pr,vl);
      else {
	Qstrom* qu3;
	int u=unif_seznamprom(pp->vobsah(),qu2,qu3,pp->delka(),pr,vl);
	if(u) return unif_seznam(qu1->vbratr(),qu3,pr,vl); else
	{
	  if(pr)
	    if(vl) pr->smaz(ps); else pr->smaz(NULL);
	  return 0;
	}
      }
    }
  } else {
    if(!unif_strom(qu1,qu2,pr,vl)) return 0;
    else return unif_seznam(qu1->vbratr(),qu2->vbratr(),pr,vl);
  }
}

int unif_seznamprom(Qstrom* qu1,Qstrom*qu2,Qstrom*& qu3,int dlk,Promenne* pr,Volba*& vl)
{
  if(!qu2) return 0;
  else if(!unif_strom(qu1,qu2,pr,vl)) return 0;
  else if(dlk==1) { qu3=qu2->vbratr(); return 1; }
  else return unif_seznamprom(qu1->vbratr(),qu2->vbratr(),qu3,dlk-1,pr,vl);
}

int unif_retezec(Qhrana* qu1,Qhrana* qu2,Promenne* pr,Volba*& vl)
{
  if(!qu1 && !qu2) return 1;
  else if(qu1 && !qu2 || !qu1 && qu2) return 0;
  else {
    if(!unif_strom(qu1->vstrom(),qu2->vstrom(),pr,vl)) return 0;
    else return unif_retezec(qu1->vretez1(),qu2->vretez1(),pr,vl);
  }
}

int delka_seznamu(Qstrom* qu)
{
  int d=0;
  while(qu)
  {
    d++;
    qu=qu->vbratr();
  }
  return d;
}

Qstrom* posun_vseznamu(Qstrom* qu,int n)
{
  Qstrom* qv=qu;
  while(n--) qv=qv->vbratr();
  return qv;
}

int ladeni(Qprogram* qp,int faze,int pravidlo,char* retez)
{
  Promenne* pr=new Promenne();
  malopameti(pr);
  Volba* vl=NULL;
  char* fn=tmpnam(NULL);
  FILE* f=fopen(fn,"wt");
  if(!f) { QERR<<STR_ERRORTEMPFILE<<endln; return 0; }
  fputs(retez,f);
  fclose(f);
  f=fopen(fn,"rt");
  Qvstup* qv=synt_vstup(f);
  fclose(f);
  if(!qv) { QERR<<STR_CHAINERROR<<endln; return 0; }
  Qhrana* qu=qv->vhrana();
  int n=1;
  do {
    int u=unif_retezec(qp->pravidloid(faze,pravidlo)->levyretez(),qu,pr,vl);
    if(u)
      QERR<<STR_UNIF<<" ("<<n<<". "<<STR_POSSIBSUCCESS<<endln;
    else
      QERR<<STR_UNIF<<" ("<<n<<". "<<STR_POSSIBNOTSUCCESS<<endln;
    pr->vypis(n);
    n++;
  } while(vl);
  delete vl;
  delete pr;
  return 1;
}
